package com.tdk.mybatisplus.demo.common.util;

import cn.hutool.core.util.StrUtil;
import com.tdk.mybatisplus.demo.common.config.MinioConfig;
import com.tdk.mybatisplus.demo.common.exception.GlobalException;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveBucketArgs;
import io.minio.RemoveObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Component
@AllArgsConstructor
public class MinioUtil {

  @Autowired
  private MinioConfig minioConfig;
  @Autowired
  private MinioClient minioClient;

  /**
   * 初始化 minio 配置
   */
  @PostConstruct
  private void init() {
    try {
      if (StrUtil.isNotEmpty(minioConfig.getBucketName()) && !bucketExist(
          minioConfig.getBucketName())) {
        createBucket(minioConfig.getBucketName());
      }
    } catch (Exception e) {
      log.error("初始化minio配置异常: {}", e.getMessage());
    }
  }

  /**
   * 文件上传
   *
   * @param fileName 文件名
   * @param file 文件
   * @return 文件URL
   */
  public String upload(MultipartFile file, String fileName) {
    return upload(minioConfig.getBucketName(), fileName, file);
  }

  /**
   * 文件下载
   *
   * @param fileName 文件名
   */
  public FastByteArrayOutputStream download(String fileName) throws Exception {
    return download(minioConfig.getBucketName(), fileName);
  }

  /**
   * 删除文件
   *
   * @param fileName 文件名
   * @return Boolean
   */
  public Boolean remove(String fileName) {
    return remove(minioConfig.getBucketName(),fileName);
  }

  /**
   * 删除文件
   *
   * @return Boolean
   */
  public Boolean removeBatch() {
    List<Item> itemList = listObjects(minioConfig.getBucketName());
    itemList.forEach(item -> {
      int daysNum=(int)(LocalDate.now().toEpochDay()-item.lastModified().toLocalDateTime().toLocalDate().toEpochDay());
      if(daysNum>0){
        remove(minioConfig.getBucketName(),item.objectName());
      }
    });
    return Boolean.TRUE;
  }
  /**
   * 文件下载
   *
   * @param fileName 文件名
   * @return
   */
  public byte[] download2ByteArray(String fileName) throws Exception {
    FastByteArrayOutputStream outputStream = download(fileName);
    byte[]                    bytes        = outputStream.toByteArray();
    IOUtils.closeQuietly(outputStream);
    return bytes;
  }
  
  /**
   * 文件下载
   *
   * @param fileName 文件名
   * @return
   */
  public ByteArrayInputStream download2InputStream(String fileName) throws Exception {
    byte[]                    byteArray       = download2ByteArray(fileName);
    ByteArrayInputStream      byteArrayInputStream = new ByteArrayInputStream(byteArray);
    return byteArrayInputStream;
  }
    
  /**
   * 检查存储 Bucket 是否存在
   *
   * @param bucketName Bucket存储桶名称
   * @return Boolean
   */
  public Boolean bucketExist(String bucketName) {
    try {
      return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
  }

  /**
   * 创建存储 Bucket
   *
   * @param bucketName Bucket存储桶名称
   * @return Boolean
   */
  public Boolean createBucket(String bucketName) {
    checkBucketNoExist(bucketName);
    try {
      minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
    return true;
  }

  /**
   * 删除存储 Bucket
   *
   * @param bucketName Bucket存储桶名称
   * @return Boolean
   */
  public Boolean removeBucket(String bucketName) {
    //存储桶为空时才能删除成功
    try {
      List<Item> items = listObjects(bucketName);
      if (!ObjectUtils.isEmpty(items)) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
      } else {
        throw new GlobalException("删除bucket失败，请先删除bucket下的所有文件");
      }
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
    return true;
  }

  /**
   * 获取全部bucket
   *
   * @return 存储桶列表
   */
  public List<Bucket> getBuckets() {
    try {
      return minioClient.listBuckets();
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
  }

  /**
   * 文件上传
   *
   * @param bucketName Bucket存储桶名称
   * @param fileName 文件名
   * @param file 文件
   * @return 文件URL
   */
  public String upload(String bucketName, String fileName, MultipartFile file) {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    checkBucketExist(bucketName);
    try {
      PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
          .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType())
          .build();
      // 文件名称相同会覆盖
      minioClient.putObject(objectArgs);

      return getFilePath(bucketName, fileName);
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
  }

  /**
   * 获取文件路径
   *
   * @param bucketName Bucket存储桶名称
   * @param fileName 文件名
   * @return 文件URL
   */
  public String getFilePath(String bucketName, String fileName) {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    checkBucketExist(bucketName);
    // 查看文件地址
    GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName)
        .object(fileName).method(Method.GET).build();
    try {
      return minioClient.getPresignedObjectUrl(build);
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
  }


  /**
   * 文件下载
   *
   * @param fileName 文件名
   */
  public FastByteArrayOutputStream download(String bucketName, String fileName) throws Exception {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    checkBucketExist(bucketName);
    GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(fileName)
        .build();
    try (GetObjectResponse getObjectResponse = minioClient.getObject(objectArgs)) {
      byte[] buf = new byte[1024];
      int    len;
      try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
        while ((len = getObjectResponse.read(buf)) != -1) {
          os.write(buf, 0, len);
        }
        os.flush();
        return os;
      }
    }
  }

  /**
   * 文件下载
   *
   * @param bucketName Bucket存储桶名称
   * @param fileName 文件名
   * @param response HttpServletResponse
   */
  public Boolean download(String bucketName, String fileName, HttpServletResponse response) {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    checkBucketExist(bucketName);
    GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(fileName).build();
    try (GetObjectResponse getObjectResponse = minioClient.getObject(objectArgs)) {
      byte[] buf = new byte[1024];
      int    len;
      try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
        while ((len = getObjectResponse.read(buf)) != -1) {
          os.write(buf, 0, len);
        }
        os.flush();
        byte[] bytes = os.toByteArray();
        response.setCharacterEncoding("utf-8");
        // 设置强制下载不打开
        //res.setContentType("application/force-download");
        response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
        try (ServletOutputStream stream = response.getOutputStream()) {
          stream.write(bytes);
          stream.flush();
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }

  /**
   * 查询某个存储桶中的所有文件
   *
   * @param bucketName Bucket存储桶名称
   * @return 文件列表
   */
  public List<Item> listObjects(String bucketName) {
    checkBucketExist(bucketName);
    Iterable<Result<Item>> results = minioClient
        .listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
    List<Item> items = new ArrayList<>();
    try {
      for (Result<Item> result : results) {
        items.add(result.get());
      }
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
    return items;
  }
  
  /**
   * 删除文件
   *
   * @param bucketName Bucket存储桶名称
   * @param fileName 文件名
   * @return Boolean
   */
  public Boolean remove(String bucketName, String fileName) {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    bucketExist(bucketName);
    try {
      minioClient
          .removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
    return true;
  }

  /**
   * 批量删除文件
   *
   * @param bucketName Bucket存储桶名称
   * @param fileNames 文件名集合
   * @return Boolean
   */
  public Boolean remove(String bucketName, List<String> fileNames) {
    bucketName = StrUtil.emptyToDefault(bucketName.trim(), minioConfig.getBucketName());
    checkBucketExist(bucketName);
    List<DeleteObject> deleteObjects = fileNames.stream().map(DeleteObject::new)
        .collect(Collectors.toList());
    try {
      Iterable<Result<DeleteError>> deleteErrorResults = minioClient.removeObjects(
          RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build());
      for (Result<DeleteError> deleteErrorResult : deleteErrorResults) {
        DeleteError deleteError = deleteErrorResult.get();
        throw new GlobalException("删除文件{}失败,{}", deleteError.objectName(), deleteError.message());
      }
    } catch (Exception e) {
      throw new GlobalException(e.getMessage());
    }
    return true;
  }

  /**
   * 检查存储 Bucket 是否存在
   *
   * @param bucketName Bucket存储桶名称
   */
  private void checkBucketExist(String bucketName) {
    boolean flag = bucketExist(bucketName);
    if (!flag) {
      throw new GlobalException("{}不存在", bucketName);
    }
  }

  /**
   * 检查存储 Bucket 是否不存在
   *
   * @param bucketName Bucket存储桶名称
   */
  private void checkBucketNoExist(String bucketName) {
    boolean flag = bucketExist(bucketName);
    if (flag) {
      throw new GlobalException("{}已存在", bucketName);
    }
  }
}
